`timescale 1ns/1ps

module cic_filter_tb();

//从语法上看冗余的$signed()转换是多余的，但这些显式转换是防御性编程做法

parameter CLK_PERIOD = 10.417; // 96MHz时钟周期、
//parameter OUTPUT_CLK_PERIOD = 250.0; // 4MHz输出时钟周期 = 1/4M * 1e9 = 250ns
parameter DECIMATION_FACTOR = 24; // 96MHz → 4MHz降采样因子
parameter FILE_PATH = "./cic_input.txt";
parameter SAMPLE_COUNT = 96000;

reg                     clk;
reg                     rstn;
reg  [3:0]              dat_in;
wire signed [26:0]      dat_out;
wire                    clk_vld_out;

// 文件处理变量
integer file_handle;
integer scan_result;
reg [3:0] hex_value;
integer sample_count = 0;
integer output_file;
integer csv_file;

// 性能监控
reg [31:0] clock_count = 0;
reg [31:0] output_count = 0;
reg signed [26:0] max_output = 27'sd0;
reg signed [26:0] min_output = 27'sd0;
real output_sum = 0.0;

cic_filter uut (
    .clk         (clk),
    .rstn        (rstn),
    .dat_in      (dat_in),
    .dat_out     (dat_out),
    .clk_vld_out (clk_vld_out)
);

initial begin
    clk = 0;
    forever #(CLK_PERIOD/2) clk = ~clk;
end

// 创建输出文件
initial begin
    output_file = $fopen("cic_filter_v_output.txt", "w");
    csv_file = $fopen("cic_filter_results.csv", "w");
    
    if (output_file == 0 || csv_file == 0) begin
        $display("错误: 无法创建输出文件");
        $finish;
    end
    
    // 写入CSV文件头
    $fwrite(csv_file, "采样序号,有符号输出值,无符号输出,时间(ns),输入值\n");
end

// 监控时钟和有效输出信号
always @(posedge clk) begin
    clock_count <= clock_count + 1;
    
    if (clk_vld_out) begin
        output_count <= output_count + 1;
        
        $fwrite(output_file, "%0d\n", $signed(dat_out));
        $fwrite(csv_file, "%0d,%0d,%0d,%0t,%h\n", 
                output_count, $signed(dat_out), dat_out, $time, dat_in);
        
        // 更新最大/最小值跟踪
        if ($signed(dat_out) > $signed(max_output))
            max_output <= dat_out;
        if ($signed(dat_out) < $signed(min_output))
            min_output <= dat_out;
            
        // 更新用于计算平均值的总和
        output_sum = output_sum + $signed(dat_out);
        
        // 溢出检测
        if ($signed(dat_out) == {1'b1, 26'b0} || $signed(dat_out) == {1'b0, {26{1'b1}}}) begin
            $display("警告: 在时间 %0t ns 检测到可能的溢出", $time);
        end
    end
    
    // 最小化进度报告（仅每~10%显示一次）
    if (clock_count % (SAMPLE_COUNT * 0.1) == 0 && clock_count > 0) begin
        $display("进度: 大约完成 %0d%%", (clock_count*100)/(SAMPLE_COUNT));
    end
end

// 主测试过程
initial begin
    // 初始化信号
    rstn = 0;
    dat_in = 0;
    
    #(CLK_PERIOD*10);
    rstn = 1;
    #(CLK_PERIOD*10);
    
    $display("开始仿真: 时间 %0t ns", $time);

    file_handle = $fopen(FILE_PATH, "r");
    if (file_handle == 0) begin
        $display("错误: 无法打开输入文件 %s", FILE_PATH);
        $finish;
    end
    
    while (!$feof(file_handle) && sample_count < SAMPLE_COUNT) begin
        scan_result = $fscanf(file_handle, "%h\n", hex_value);
        if (scan_result == 1) begin
            dat_in = hex_value;
            sample_count = sample_count + 1;
            #(CLK_PERIOD);
        end
        else begin
            $display("警告: 文件读取格式错误，行号: %0d", sample_count+1);
            #(CLK_PERIOD);
        end
    end
    
    $fclose(file_handle);
    
    // 运行额外的周期以冲刷滤波器内部状态
    $display("\n数据输入完成，运行额外的周期来完成处理...");
    #(CLK_PERIOD * DECIMATION_FACTOR * 5);
    
    $fclose(output_file);
    $fclose(csv_file);
    
    $display("\n========== 仿真统计 ==========");
    $display("输入样本数: %0d", sample_count);
    $display("有效输出次数: %0d", output_count);
    $display("理论输出次数: %0d", sample_count / DECIMATION_FACTOR);
    $display("最终降采样比: %.3f (期望值: %0d)", 
             (output_count > 0) ? (clock_count*1.0/output_count) : 0,
             DECIMATION_FACTOR);
    $display("CIC输出范围: [%0d, %0d]", $signed(min_output), $signed(max_output));
    $display("CIC输出平均值: %.2f", (output_count > 0) ? (output_sum / output_count) : 0);
    $display("输出文件: cic_filter_v_output.txt, cic_filter_results.csv");
    $display("===========================================");
    
    $finish;
end


// 保存波形数据
initial begin
    // 不同仿真器
    `ifdef VCD_DUMP
        // VCS, Icarus Verilog
        $dumpfile("cic_filter_tb.vcd");
        $dumpvars(0, cic_filter_tb);
    `endif
    
    `ifdef FSDB_DUMP
        // Synopsys VCS with FSDB
        $fsdbDumpfile("cic_filter_tb.fsdb");
        $fsdbDumpvars(0, cic_filter_tb); 
    `endif
    
    `ifdef WLF_DUMP
        // ModelSim/Questa
        $wlfdumpvars(0, cic_filter_tb);
    `endif
    
    $display("开始记录波形...");
end

endmodule